import { _decorator, Layers, Node, NodeEventType, TransformBit, UITransform } from 'cc';
import { removeIf } from "@coldcloude/kai2";
const { ccclass } = _decorator;

@ccclass('NodeEx')
export class NodeEx extends Node {

    constructor(name?:string){
        super(name)
    }

    protected fixSiblingChanges (): number[] {
        const changed: number[] = [];
        for (let i = 0; i < this._children.length; ++i) {
            const node = this._children[i];
            if (node._siblingIndex !== i) {
                node._siblingIndex = i;
                changed.push(i);
            }
        }
        return changed;
    }

    /**
     * @en Batch update the children's sibling index to the index of in children, and emit events.
     * @zh 批量更新当前节点子节点的sibling index为其在children数组中的下标，并触发事件。
     */
    protected emitSiblingIndicesChanges (changed: number[]): void {
        if (changed.length > 0) {
            try {
                this.emit(NodeEventType.CHILDREN_ORDER_CHANGED);
            } catch(e) {
                console.error(e);
            }
            for (const i of changed) {
                try {
                    const node = this._children[i];
                    if (node._onSiblingIndexChanged) {
                        node._onSiblingIndexChanged(i);
                    }
                    node._eventProcessor.onUpdatingSiblingIndex();
                } catch (e) {
                    console.error(e);
                }
            }
        }
    }

    protected emitChildrenRemoves (nodes: Node[]): void {
        for(const node of nodes){
            try {
                node.invalidateChildren(TransformBit.TRS);
                if(node.emit){
                    node.emit(NodeEventType.PARENT_CHANGED, this);
                }
                this.emit(NodeEventType.CHILD_REMOVED, node);
            } catch (e) {
                console.error(e);
            }
        }
    }

    /**
     * @en Sort the children according to the comparator.
     * @zh 根据比较器对当前节点的子节点全排序
     * @param comp @en A comparator that return negtive value / 0 value / positive value
     * when the first node is less than / equals to / greater than the second node.
     * @zh 一个比较器，当第一个节点小于/等于/大于第二个节点时，返回负值/0值/正值。
     * @return All new index that have changed.
     */
    public sortChildren (sort: (ns: Node[]) => void): number[] {
        sort(this._children);
        const changes = this.fixSiblingChanges();
        this.emitSiblingIndicesChanges(changes);
        return changes;
    }

    public removeChildren (test: (n: Node) => boolean): {
        removes: Node[],
        changes: number[]
    } {
        const removes = removeIf(this._children, test);
        const changes = this.fixSiblingChanges();
        this.emitChildrenRemoves(removes);
        this.emitSiblingIndicesChanges(changes);
        return {
            removes: removes,
            changes: changes
        }
    }

    public removeAndSortChildren (test: (n: Node) => boolean, sort: (ns: Node[]) => void): {
        removes: Node[],
        changes: number[]
    } {
        const removes = removeIf(this._children, test);
        sort(this._children);
        const changes = this.fixSiblingChanges();
        this.emitChildrenRemoves(removes);
        this.emitSiblingIndicesChanges(changes);
        return {
            removes: removes,
            changes: changes
        }
    }
}

export function buildUINodeEx(ax?:number,ay?:number){
    const node = new NodeEx();
    node.layer = Layers.Enum.UI_2D;
    const ui = node.addComponent(UITransform);
    ui.setAnchorPoint(ax===undefined?0:ax,ay===undefined?0:ay);
    return node;
}
